PT NAD
Интеграция с Positive Technologies Network Attack Discovery (PT NAD)
Данное руководство описывает быструю настройку интеграции Solar TI Feeds Agent с PT NAD для автоматической загрузки индикаторов компрометации (IoC) через промежуточное хранение в локальной базе данных.
Предварительные требования
- Установленный и настроенный Solar TI Feeds Agent
- Учётные данные для доступа к API PT NAD (логин и пароль), если учетная запись была создана в PT NAD или JWT-токен, если аутентификация происходит через PT MC
- Доступ к серверу Solar TI Feeds с действительным JWT-токеном
Конфигурация интеграции
Интеграция реализована несколькими пайплайнами, которые обеспечивают надёжное получение, промежуточное хранение и доставку индикаторов в PT NAD, включая повторную обработку неудачных попыток. Аутентификация в PT NAD выполняется через HTTP Basic Auth (логин/пароль).
Основные компоненты интеграции:
| Компонент | Назначение |
|---|---|
FeedsAPIGenerator | Загрузка данных из Solar TI Feeds API |
FeedsDBSink | Сохранение данных в локальную SQLite-базу |
FeedsDBGenerator | Чтение данных из базы для обработки |
FeedsFailedDBGenerator | Повторная обработка ошибочных записей |
Filter | Фильтрация индикаторов по заданным условиям (тип и зона) |
Mapper | Преобразование JSON-данных в структуру, ожидаемую PT NAD |
NADSink | Отправка подготовленных данных в PT NAD |
Переменные окружения
Для работы интеграции необходимо определить следующие переменные окружения в docker-compose.yml:
| Переменная | Назначение |
|---|---|
TIC_AGENT_NAD_ADDRESS | Полный URL сервера PT NAD |
TIC_AGENT_NAD_USER | Имя пользователя для Basic Auth в API PT NAD |
TIC_AGENT_NAD_PASSWORD | Пароль для Basic Auth в API PT NAD |
TIC_AGENT_NAD_TOKEN | JWT-токен для аутентификации, если она происходит через PT MC |
TIC_AGENT_NAD_PROXY | Строка подключения к прокси-серверу для PT NAD (например, socks5://user:password@proxy.domain:port) |
Структура пайплайнов для PT NAD
Pipeline 1: feeds-api (загрузка данных)
| Компонент | Параметры | Описание |
|---|---|---|
FeedsAPIGenerator | name = "feeds-api-generator" | Запрос индикаторов из Solar TI Feeds API с заданным интервалом и размером пакета. Типы network включают IP-адреса, домены и URL. |
server = templates.TICApiCfg (адрес и токен из TIC_AGENT_API_URL / TIC_AGENT_API_TOKEN) | ||
schedule = (read?("env:TIC_AGENT_API_POOL_INTERVAL") ?? 3600).toInt().s | ||
filter.types = new Listing md5 | ||
pagination.limit = (read?("env:TIC_AGENT_API_BATCH_SIZE") ?? 1000).toInt() | ||
FeedsDBSink | name = "feeds-db-sink" | Сохранение всех полученных данных в SQLite-базу для последующей обработки. |
inputs = ["feeds-api-generator"] |
Pipeline 2–9: *-feeds-nad (обработка и выгрузка)
Для каждого репутационного списка создаётся отдельный пайплайн с именем вида <listName>-feeds-nad.
Список создаваемых репутационных списков:
| listName | Тип индикатора | Цвет | Условие фильтрации (зона) |
|---|---|---|---|
suspicious-ip | ip | orange | SUSPICIOUS |
malicious-ip | ip | red | MALICIOUS |
suspicious-domains | dn | orange | SUSPICIOUS |
malicious-domains | dn | red | MALICIOUS |
suspicious-urls | uri | orange | SUSPICIOUS |
malicious-urls | uri | red | MALICIOUS |
suspicious-hashes | md5 | orange | SUSPICIOUS |
malicious-hashes | md5 | red | MALICIOUS |
Параметры компонентов для каждого пайплайна:
| Компонент | Параметры | Описание |
|---|---|---|
FeedsDBGenerator | name = "<listName>-nad-feeds-db-generator" | Чтение индикаторов из БД с настраиваемыми интервалом, задержкой и размером пакета. |
schedule = (read?("env:TIC_AGENT_DB_POOL_INTERVAL") ?? 600).toInt().s | ||
delay = (read?("env:TIC_AGENT_DB_START_DELAY") ?? 60).toInt().s | ||
batchSize = (read?("env:TIC_AGENT_DB_BATCH_SIZE") ?? 100).toInt() | ||
batchDelay = (read?("env:TIC_AGENT_DB_BATCH_DELAY") ?? 100).toInt().ms | ||
FeedsFailedDBGenerator | name = "<listName>-nad-feeds-db-failed-generator" | Повторная обработка индикаторов, отправка которых ранее завершилась ошибкой. Привязан к основному генератору через feedGenName. |
feedGenName = "<listName>-nad-feeds-db-generator" | ||
Filter | name = "<listName>-filter" | Оставляет только индикаторы, соответствующие типу и зоне (например, для suspicious-ip – IP-адреса с зоной SUSPICIOUS). |
inputs = ["<listName>-nad-feeds-db-generator", "<listName>-nad-feeds-db-failed-generator"]filters – условие вида:upper(type) in ["IPV4","IPV6","SOCKETV4","SOCKETV6"] && upper(zone) in ["SUSPICIOUS"](для каждого списка – свой набор типов и зона) | ||
Mapper | name = "<listName>-mapper" | Преобразует данные: вычисляет итоговое действие (UPDATE или DELETE) на основе исходных действий в поле feeds и оставляет поля value и action. |
inputs = ["<listName>-filter"] | ||
jsonpath = { "actions" = "$.feeds[*]..action" } | ||
rules = { "value" = "value", "action" = "any(actions, {# in ["UPDATE", "CREATE"]}) ? "UPDATE" : "DELETE"" } | ||
NADSink | name = "<listName>-nad-sink" | Отправляет подготовленные индикаторы в PT NAD в соответствующий репутационный список. Аутентификация – Basic Auth. |
inputs = ["<listName>-mapper"] | ||
server = new agent.ServerCfg { | ||
color = "<listColor>" (orange/red) | ||
type = "<listType>" (ip/dn/uri/md5) | ||
externalKey = "4RAYS-<listName>" |
Логика работы:
- Фильтр пропускает индикаторы, у которых тип соответствует заданному (IP, домен, URL или хеш) и зона равна
SUSPICIOUSилиMALICIOUS(в зависимости от списка). - В маппере вычисляется поле
action: если среди исходных действий естьUPDATEилиCREATE, итоговое действие становитсяUPDATE, иначе –DELETE. - В зависимости от уровня угрозы объект направляется в репутационный список цветов
orange(для подозрительных) илиred(для вредоносных). При этом фиксируются тип объекта (ip,dn,uri,md5) и название вида4RAYS-<listName>.
Справочник параметров для NADSink
Параметры ServerCfg (подключение к PT NAD)
| Параметр | Тип данных | Обязательность | Описание | Пример |
|---|---|---|---|---|
credentials | BasicAuth | Да | Аутентификация по логину/паролю. | credentials = new BasicAuth { username = read("env:TIC_AGENT_NAD_USER") password = read("env:TIC_AGENT_NAD_PASSWORD") } |
address | String | Да | Полный URL API PT NAD | address = read("env:TIC_AGENT_NAD_ADDRESS") |
insecureSkipVerify | Boolean | Нет | Отключение проверки SSL-сертификата | insecureSkipVerify = true |
Параметры NADSink
| Параметр | Тип данных | Обязательность | Описание |
|---|---|---|---|
server | ServerCfg | Да | Конфигурация подключения (адрес и учётные данные) |
color | String | Да | Цвет репутационного списка, в который загружаются индикаторы (orange или red) |
type | String | Да | Тип индикаторов (ip, dn, uri, md5) |
externalKey | String | Да | Идентификатор внешнего списка в PT NAD (например, "4RAYS-suspicious-ip") |
Полный пример конфигурационного файла для интеграции с PT NAD
📄 Нажмите, чтобы показать/скрыть nad_pipeline.pkl
amends "package://pkg.pkl-lang.org/github.com/pipelane/pipelaner/pipelaner@1.3.1#/Pipelaner.pkl"
import "package://pkg.pkl-lang.org/github.com/pipelane/pipelaner/pipelaner@1.3.1#/source/Components.pkl"
import "../pkl/internal/agent.pkl"
import "../pkl/internal/templates.pkl"
local repLists =
List(
new RepListParams {
listName = "suspicious-ip"
filter =
"""
upper(type) in ["IPV4", "IPV6", "SOCKETV4", "SOCKETV6"] && upper(zone) in ["SUSPICIOUS"]
"""
listType = "ip"
listColor = "orange"
},
new RepListParams {
listName = "malicious-ip"
filter =
"""
upper(type) in ["IPV4", "IPV6", "SOCKETV4", "SOCKETV6"] && upper(zone) in ["MALICIOUS"]
"""
listType = "ip"
listColor = "red"
},
new RepListParams {
listName = "suspicious-domains"
filter =
"""
upper(type) in ["DOMAIN"] && upper(zone) in ["SUSPICIOUS"]
"""
listType = "dn"
listColor = "orange"
},
new RepListParams {
listName = "malicious-domains"
filter =
"""
upper(type) in ["DOMAIN"] && upper(zone) in ["MALICIOUS"]
"""
listType = "dn"
listColor = "red"
},
new RepListParams {
listName = "suspicious-urls"
filter =
"""
upper(type) in ["URL"] && upper(zone) in ["SUSPICIOUS"]
"""
listType = "uri"
listColor = "orange"
},
new RepListParams {
listName = "malicious-urls"
filter =
"""
upper(type) in ["URL"] && upper(zone) in ["MALICIOUS"]
"""
listType = "uri"
listColor = "red"
},
new RepListParams {
listName = "suspicious-hashes"
filter =
"""
upper(type) in ["MD5"] && upper(zone) in ["SUSPICIOUS"]
"""
listType = "md5"
listColor = "orange"
},
new RepListParams {
listName = "malicious-hashes"
filter =
"""
upper(type) in ["MD5"] && upper(zone) in ["MALICIOUS"]
"""
listType = "md5"
listColor = "red"
},
)
local class RepListParams {
listName: String
filter: String
listType: agent.NadListType
listColor: agent.NadListColor
}
local feedApiPipeline = new Components.Pipeline {
name = "feeds-api"
inputs {
new agent.FeedsAPIGenerator {
name = "feeds-api-generator"
server = templates.TICApiCfg
schedule = (read?("env:TIC_AGENT_API_POOL_INTERVAL") ?? 3600).toInt().s
filter = new agent.FeedsAPIRequestParams {
types = new Listing {
"network"
"md5"
}
pagination = new agent.Pagination {
limit = (read?("env:TIC_AGENT_API_BATCH_SIZE") ?? 1000).toInt()
}
}
}
}
sinks {
new agent.FeedsDBSink {
name = "feeds-db-sink"
inputs {
"feeds-api-generator"
}
}
}
}
local nadPipelines =
repLists.map((listParam) -> new Components.Pipeline {
name = listParam.listName + "-nad"
inputs {
new agent.FeedsDBGenerator {
name = listParam.listName + "-nad-feeds-db-generator"
schedule = (read?("env:TIC_AGENT_DB_POOL_INTERVAL") ?? 600).toInt().s
delay = (read?("env:TIC_AGENT_DB_START_DELAY") ?? 60).toInt().s
batchSize = (read?("env:TIC_AGENT_DB_BATCH_SIZE") ?? 100).toInt()
batchDelay = (read?("env:TIC_AGENT_DB_BATCH_DELAY") ?? 100).toInt().ms
}
new agent.FeedsDBFailedGenerator {
name = listParam.listName + "-nad-feeds-db-failed-generator"
feedGenName = listParam.listName + "-nad-feeds-db-generator"
}
}
transforms {
new agent.Filter {
name = listParam.listName + "-filter"
inputs {
listParam.listName + "-nad-feeds-db-generator"
listParam.listName + "-nad-feeds-db-failed-generator"
}
filters = new Listing {
listParam.filter
}
}
new agent.Mapper {
name = listParam.listName + "-mapper"
inputs {
listParam.listName + "-filter"
}
jsonpath = new Mapping {
["actions"] = "$.feeds[*]..action"
}
rules = new Mapping {
["value"] = "value"
["action"] =
"""
any(actions, {# in ["UPDATE", "CREATE"]}) ? "UPDATE" : "DELETE"
"""
}
}
}
sinks {
new agent.NADSink {
name = listParam.listName + "-nad-sink"
inputs {
listParam.listName + "-mapper"
}
server = new agent.ServerCfg {
credentials = new agent.BasicAuth {
username = read("env:TIC_AGENT_NAD_USER")
password = read("env:TIC_AGENT_NAD_PASSWORD")
}
address = read("env:TIC_AGENT_NAD_ADDRESS")
proxy = read?("env:TIC_AGENT_NAD_PROXY")
insecureSkipVerify = true
}
color = listParam.listColor
type = listParam.listType
externalKey = "4RAYS-" + listParam.listName
}
}
})
pipelines = nadPipelines.add(feedApiPipeline).toListing()
settings = templates.baseSettings
Мониторинг результатов работы интеграции
После запуска агента убедитесь в корректности работы:
| Проверка | Действие |
|---|---|
| Проверка состояния БД | Убедитесь, что SQLite-база наполняется индикаторами, а успешно отправленные записи помечаются соответствующим образом. |
| Анализ логов | Проверьте логи агента (в директории, заданной шаблонами) на наличие ошибок подключения к PT NAD или Feeds API. |
| Верификация в PT NAD | В интерфейсе PT NAD убедитесь, что созданы репутационные списки с заданными externalKey (например, 4RAYS-suspicious-ip, 4RAYS-malicious-domains и т.д.) и что они содержат актуальные индикаторы с соответствующими цветами (orange или red). |
| Контроль очереди повторов | При временных сбоях индикаторы автоматически повторно отправляются через 1 минуту. Убедитесь, что после восстановления связи они доставляются. |
Примечание: Логи агента также можно просмотреть в реальном времени с помощью команды
docker logs {CONTAINER_NAME}.